
package kalkon;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import nu.xom.Attribute;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Elements;
import nu.xom.Node;
import nu.xom.Nodes;
import nu.xom.Serializer;
import nu.xom.XPathContext;

/**
 *
 * @author tommz tommz9@gmail.com
 * @author newohybat newohybat@gmail.com
 */
public class InternalRepresentation {

    public static String databaseNs = "http://code.google.com/p/kalkon/database";
    public static String concertsNs = "http://code.google.com/p/kalkon/internalConcerts";
    public static String clubNs = "http://code.google.com/p/kalkon/internalClub";
    Document database;
    XPathContext context;

    private InternalRepresentation(Document doc) {
        database = doc;
        context = new XPathContext();

        context.addNamespace("d", databaseNs);
        context.addNamespace("c", clubNs);
        context.addNamespace("e", concertsNs);
    }

    /**
     * Factory method.
     * @param is database file
     * @return InternalRepresentation or null in case of some errors.
     */
    public static InternalRepresentation getRepresentation(InputStream is) {

        Document doc = ConfigurationLoader.loadXMLvalidate(is);

        if(doc == null){
            return null;
        }

        return new InternalRepresentation(doc);

    }

    /**
     * Gets list of ids saved in internal database.
     * @return
     */
    public List<String> getClubsIDs(){

        Element root = database.getRootElement();

        Nodes nodes = root.query("//c:club/@id", context);

        List<String> ret = new ArrayList<String>();

        for(int i = 0; i < nodes.size(); i++){
            Node n = nodes.get(i);
            assert n instanceof Attribute;
            ret.add(n.getValue());
        }

        return ret;

    }

    /**
     *
     * @param clubId
     * @return true if there is a club with clubId in the database.
     */
    public boolean hasClub(String clubId){

        return 0 < database.query("//c:club[@id = '"+clubId+"']", context).size();

    }

    /**
     * Adds club to the database.
     * @param club
     */
    public void addClub(Document club){

        Element clubEntry = new Element("clubEntry", databaseNs);

        clubEntry.appendChild(new Element(club.getRootElement()));

        Element concerts = new Element("concerts", concertsNs);

        concerts.addAttribute(new Attribute("id",club.getRootElement().getAttributeValue("id")));

        clubEntry.appendChild(concerts);
        
        database.getRootElement().appendChild(clubEntry);

    }

    /**
     * Adds concerts to the database. Skips concerts which are already in the database.
     * @param clubId
     * @param concerts
     * @return
     */
    public boolean addConcerts(String clubId, Document concerts){

        if(clubId == null || concerts == null){
            return false;
        }

        Nodes nodes = database.query("//d:clubEntry[c:club[@id='"+clubId+"']]", context);

        // check if there is a club with such id in the database
        if(nodes.size() != 1){
            return false;
        }

        Element clubEntry = (Element) nodes.get(0);

        Elements newConcerts = concerts.getRootElement().getChildElements("concert", concertsNs);

        for(int i=0; i < newConcerts.size(); i++){
            Element newConcert = newConcerts.get(i);
            String concertStartDay = newConcert.getFirstChildElement("day_start", concertsNs).getValue();
            // for every new concert finds, if it is already in the database
            if(0 < clubEntry.query("e:concerts/e:concert[e:day_start=\""+concertStartDay+"\"]", context).size()){
                // already in the database
                continue;
            }

            // add it

            clubEntry.getFirstChildElement("concerts", concertsNs).appendChild(new Element(newConcert));

        }

        return true;

    }

    /**
     * Saves database to xml file.
     * @param os
     * @return
     */
    public boolean saveRepresentation(OutputStream os) {

        try {
            Serializer serializer = new Serializer(os, "UTF-8");
            serializer.setIndent(4);
            serializer.setMaxLength(64);
            serializer.write(database);
        } catch (IOException ex) {
            System.err.println(ex);
            return false;
        }

        return true;

    }

    /**
     * Returns Document with database.
     * @return
     */
    public Document getDocument(){
        return database;
    }

    /**
     * Gets events for club specified by id.
     * @param id
     * @return
     */
    public Document getEventsForId(String id){

        Nodes e = database.query("//e:concerts[../c:club[@id='"+id+"']]", context);

        if(e.size() != 1){
            return null;
        }

        Document ret = new Document(new Element((Element) e.get(0)));

        return ret;
    }
}
